package com.ruoyi.web.controller.miniprogram;

import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.config.WxPayV3Config;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.system.SysBoat;
import com.ruoyi.common.core.domain.entity.system.UserBoatOrderInfo;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.domain.model.WxRefundReqBody;
import com.ruoyi.common.enums.BoatStatus;
import com.ruoyi.pay.service.IWxPayV2Service;
import com.ruoyi.pay.service.IWxPayV3Service;
import com.ruoyi.system.service.ISysBoatService;
import com.ruoyi.system.service.ISysJishiOrderService;
import com.ruoyi.system.service.ISysTaocanOrderService;
import com.ruoyi.system.service.IUserBoatOrderInfoService;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@RestController
@RequestMapping("/miniprogram/v1/wxpay")
public class MiniWxPayController extends BaseController {


    @Autowired
    private IWxPayV2Service wxPayV2Service;

    @Autowired
    private IWxPayV3Service wxPayV3Service;

    @Autowired
    private WxPayV3Config wxPayV3Config;

    @Autowired
    private IUserBoatOrderInfoService userBoatOrderInfoService;

    @Autowired
    private ISysJishiOrderService sysJishiOrderService;

    @Autowired
    private ISysTaocanOrderService sysTaocanOrderService;

    @Autowired
    private ISysBoatService sysBoatService;


    @Data
    static class OutOrderNoRequestBody {
        /**
         * 商户系统内部订单编号。
         */
        private String outOrderNo;
    }

    @Data
    static class PrePayReqBody {
        /**
         * 订单id。
         */
        String orderId;
        /**
         * 订单类型。0：计时收费订单，1：套餐收费订单，2：拼团订单。
         */
        int orderType;
        /**
         * 支付类型。0：仅支付押金，1：仅支付订单费用，2：支付押金+费用
         */
        int payType;
    }

    /**
     * 创建预支付订单。
     *
     * @param reqBody
     * @return
     */
    @PostMapping("/prePay")
    public AjaxResult prePay(@RequestBody PrePayReqBody reqBody) {
        String orderId = reqBody.getOrderId();
        //订单类型。0：计时收费订单，1：套餐收费订单，2：拼团订单。
        int orderType = reqBody.getOrderType();
        //支付类型 (0：仅支付押金，1：仅支付订单费用，2：支付押金+费用，3：支付超时费用)
        int payType = reqBody.getPayType();

        //如果支付类型为：0：仅支付押金 或 2：支付押金+费用这两种时，在支付前需先判断下当前订单对应的船只此时是否处于"支付倒计时中"或"空闲"状态，如果不是则不允许支付。比如离线等状态时就不允许支付。
        if (payType == 0 || payType == 2) {
            LoginUser loginUser = getLoginUser();
            if (Objects.nonNull(loginUser)) {
                Long userId = loginUser.getUserId();
                UserBoatOrderInfo userBoatOrderInfo = userBoatOrderInfoService.selectUserBoatOrderInfoByUserId(userId);
                if (Objects.nonNull(userBoatOrderInfo)) {
                    Long boatId = userBoatOrderInfo.getBoatId();
                    SysBoat sysBoat = sysBoatService.selectSysBoatByBoatId(boatId);
                    if (Objects.nonNull(sysBoat)) {
                        int boatStatus = sysBoat.getStatus();
                        if (boatStatus == BoatStatus.PayCounting.getCode() || boatStatus == BoatStatus.Idle.getCode()) {
                            //船只处于"支付倒计时中"或“空闲”状态才允许支付
                            return success(wxPayV3Service.prePay(orderId, orderType, payType));
                        }
                    }
                }
            }
        } else {
            return success(wxPayV3Service.prePay(orderId, orderType, payType));
        }

        return error("船只状态异常！");
    }

    /**
     * 客户端通知服务端用户已完成支付。
     *
     * @param outOrderNoRequestBody
     * @param payType               订单类型。0：计时收费订单，1：套餐收费订单，2：拼团订单。
     * @return
     */
//    @PostMapping("/completePay/{payType}")
//    public AjaxResult completePay(@RequestBody OutOrderNoRequestBody outOrderNoRequestBody, @PathVariable("payType") int payType) {
////        log.info("outOrderNoRequestBody: {}, payType: {}", outOrderNoRequestBody, payType);
//        String outOrderNo = outOrderNoRequestBody.getOutOrderNo();
//        wxPayV3Service.completePay(outOrderNo, payType);
//
//        return success();
//    }

    /**
     * 查询微信支付状态统一接口。
     *
     * @return
     */
//    @GetMapping("/queryPayStatus/{orderNo}")
//    public AjaxResult queryPayStatus(@PathVariable("orderNo") String orderNo) throws Exception {
//        return success(wxPayV3Service.queryPayStatus(orderNo));
//    }

    /**
     * 申请退款。
     * 注意：软件上线前必须注释掉该接口，不能给用户开放主动申请退款的功能。
     *
     * @param refundRequestBody
     * @return
     */
    @PostMapping("/refund")
    public AjaxResult refund(@RequestBody WxRefundReqBody refundRequestBody) {
        return success(wxPayV3Service.refund(refundRequestBody));
    }

    /**
     * 查询退款状态。
     *
     * @param outRefundNo 商户退款单号
     * @return
     */
    @GetMapping("/queryRefundStatus/{outRefundNo}")
    public AjaxResult queryRefundStatus(@PathVariable("outRefundNo") String outRefundNo) {
        return success(wxPayV3Service.queryRefundStatus(outRefundNo));
    }

    /**
     * 测试接口。
     *
     * @param request
     * @param response
     */
    @PostMapping("/testApi")
    public void testApi(HttpServletRequest request, HttpServletResponse response) {
        //获取请求体
        String reqBody = getRequestBody(request);
        JSONObject parseObject = JSONObject.parseObject(reqBody);
        int type = parseObject.getIntValue("type");
        if (type == 0) {
            response.setStatus(HttpStatus.HTTP_OK);
        } else {
            Map<String, Object> result = new HashMap<>();
            try {
                response.setStatus(HttpStatus.HTTP_FORBIDDEN);
                PrintWriter printWriter = response.getWriter();
                result.put("code", "FAIL");
                result.put("message", "失败");
                printWriter.write(JSONObject.toJSONString(result));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 该接口提供给微信支付官方调用，在用户使用微信支付后，微信支付官方通过调用该接口将支付结果通知给平台。
     *
     * @return
     */
    @PostMapping("/notifyPayResult1")
    public void notifyPayResult(HttpServletRequest request, HttpServletResponse response) {
        log.info("notifyPayResult execute.");
        try {
            // 从请求头中获取信息
            // 这是微信回调固定在请求头里的参数，验签要用
            //应答时间戳
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            //应答随机串
            String nonceStr = request.getHeader("Wechatpay-Nonce");
            //微信支付的应答签名
            String signature = request.getHeader("Wechatpay-Signature");
            String singType = request.getHeader("Wechatpay-Signature-Type");
            //证书序列号（微信平台）
            String serialNumber = request.getHeader("Wechatpay-Serial");
            //原始应答报文主体。注意：如果您使用了某个框架，要确保它不会篡改报文主体。对报文主体的任何篡改都会导致验证失败。
            String body = getRequestBody(request);

            //构造签名串：
            //应答时间戳\n
            //应答随机串\n
            //应答报文主体\n
            String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));

            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 从证书管理器中获取verifier

            Verifier verifier = certificatesManager.getVerifier(wxPayV3Config.getMchId());
            boolean verifyResult = verifier.verify(serialNumber, signStr.getBytes(StandardCharsets.UTF_8), signature);
            if (verifyResult) { //验签通过
                //解密数据
                String plainBody = decryptBody(body);
                log.info("notifyPayResult 解密后的明文:{}", plainBody);

                JSONObject parseObject = JSONObject.parseObject(plainBody);
                if (Objects.nonNull(parseObject)) {
                    String outOrderNo = parseObject.getString("out_trade_no");
                    JSONObject amount = parseObject.getJSONObject("amount");
                    int payerTotal = amount.getIntValue("payer_total");
                    String attachStr = parseObject.getString("attach");
                    JSONObject wxPayAttachObject = JSONObject.parseObject(attachStr);
                    if (Objects.nonNull(wxPayAttachObject)) {
                        String orderId = wxPayAttachObject.getString("orderId");
                        int orderType = wxPayAttachObject.getIntValue("orderType");
                        int payType = wxPayAttachObject.getIntValue("payType");
                        //支付成功后的处理方法。
                        wxPayV3Service.executeAfterPaySucceed(outOrderNo, payerTotal, orderId, orderType, payType);

                        //解密成功，设置响应状态码为200即可，无需设置响应体。
                        response.setStatus(HttpStatus.HTTP_OK);
                        return;
                    }
                }
            } else { //验签未通过

            }

        } catch (Exception e) {
            log.error(e.getMessage());
//            throw new RuntimeException(e);
        }

        Map<String, Object> result = new HashMap<>();
        try {
            response.setStatus(HttpStatus.HTTP_FORBIDDEN);
            PrintWriter printWriter = response.getWriter();
            result.put("code", "FAIL");
            result.put("message", "失败");
            printWriter.write(JSONObject.toJSONString(result));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 该接口提供给微信支付官方调用，在申请退款后，微信支付官方通过调用该接口将退款结果通知给平台。
     *
     * @return
     */
//    @PostMapping("/notifyRefundResult")
//    public void notifyRefundResult(HttpServletRequest request, HttpServletResponse response) {
//        log.info("notifyRefundResult execute.");
//        try {
//            // 从请求头中获取信息
//            // 这是微信回调固定在请求头里的参数，验签要用
//            //应答时间戳
//            String timestamp = request.getHeader("Wechatpay-Timestamp");
//            //应答随机串
//            String nonceStr = request.getHeader("Wechatpay-Nonce");
//            //微信支付的应答签名
//            String signature = request.getHeader("Wechatpay-Signature");
//            String singType = request.getHeader("Wechatpay-Signature-Type");
//            //证书序列号（微信平台）
//            String serialNumber = request.getHeader("Wechatpay-Serial");
//            //原始应答报文主体。注意：如果您使用了某个框架，要确保它不会篡改报文主体。对报文主体的任何篡改都会导致验证失败。
//            String body = getRequestBody(request);
//
//            //构造签名串：
//            //应答时间戳\n
//            //应答随机串\n
//            //应答报文主体\n
//            String signStr = Stream.of(timestamp, nonceStr, body).collect(Collectors.joining("\n", "", "\n"));
//
//            CertificatesManager certificatesManager = CertificatesManager.getInstance();
//            // 从证书管理器中获取verifier
//
//            Verifier verifier = certificatesManager.getVerifier(wxPayV3Config.getMchId());
//            boolean verifyResult = verifier.verify(serialNumber, signStr.getBytes(StandardCharsets.UTF_8), signature);
//            if (verifyResult) { //验签通过
//                //解密数据
//                String plainBody = decryptBody(body);
//                log.info("notifyRefundResult 解密后的明文:{}", plainBody);
//
//                JSONObject parseObject = JSONObject.parseObject(plainBody);
//                if (Objects.nonNull(parseObject)) {
//                    String outOrderNo = parseObject.getString("out_trade_no");
//                    JSONObject amount = parseObject.getJSONObject("amount");
//                    int payerTotal = amount.getIntValue("payer_total");
//                    String attachStr = parseObject.getString("attach");
//                    JSONObject wxPayAttachObject = JSONObject.parseObject(attachStr);
//                    if (Objects.nonNull(wxPayAttachObject)) {
//                        String orderId = wxPayAttachObject.getString("orderId");
//                        int orderType = wxPayAttachObject.getIntValue("orderType");
//                        int payType = wxPayAttachObject.getIntValue("payType");
//                        //支付成功后的处理方法。
//                        wxPayV3Service.executeAfterPaySucceed(outOrderNo, payerTotal, orderId, orderType, payType);
//
//                        //解密成功，设置响应状态码为200即可，无需设置响应体。
//                        response.setStatus(HttpStatus.HTTP_OK);
//                        return;
//                    }
//                }
//            } else { //验签未通过
//
//            }
//
//        } catch (Exception e) {
//            log.error(e.getMessage());
////            throw new RuntimeException(e);
//        }
//
//        Map<String, Object> result = new HashMap<>();
//        try {
//            response.setStatus(HttpStatus.HTTP_FORBIDDEN);
//            PrintWriter printWriter = response.getWriter();
//            result.put("code", "FAIL");
//            result.put("message", "失败");
//            printWriter.write(JSONObject.toJSONString(result));
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
//    }

    // 获取请求头里的数据
    private String getRequestBody(HttpServletRequest request) {
        StringBuffer sb = new StringBuffer();
        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            log.error("读取数据流异常:{}", e.getMessage());
        }
        return sb.toString();
    }

    /**
     * 解密body的密文
     * <p>
     * "resource": {
     * "original_type": "transaction",
     * "algorithm": "AEAD_AES_256_GCM",
     * "ciphertext": "",
     * "associated_data": "",
     * "nonce": ""
     * }
     *
     * @param body
     * @return
     */
    private String decryptBody(String body) throws UnsupportedEncodingException, GeneralSecurityException {
        AesUtil aesUtil = new AesUtil(wxPayV3Config.getApiKey3().getBytes(StandardCharsets.UTF_8));

        JSONObject object = JSONObject.parseObject(body);
        JSONObject resource = object.getJSONObject("resource");
        String ciphertext = resource.getString("ciphertext");
        String associatedData = resource.getString("associated_data");
        String nonce = resource.getString("nonce");

        return aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
    }
}
